﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Client.Forms.UMessageBox;
using Controls.LiveControl;
using Controls.Menu;
using Managers;
using Managers.FileTransfer.Client;
using Managers.FileTransfer.Server;
using Managers.LiveControl.Client;
using Model.LiveControl;
using Network;
using Network.Messages.LiveControl;
using Network.Messages.LiveControl.C2S;
using Network.Messages.LiveControl.S2C;
using Providers.LiveControl.Client;
using static Providers.LiveControl.Server.WindowApi;
using Message = System.Windows.Forms.Message;

namespace Client.Forms
{
    public partial class FormLiveControl : Form, IBoundsSetor
    {

        private readonly Button[] _btnArry = new Button[4];
        private readonly MousePolicy _policy;
        private readonly bool _showRegionOutlines = false;
        private bool _flagEventInit;


        private bool _flagGetWindow;
        public CloseType CloseType = CloseType.UserClose;

        private int currentFramenum = -1;

        private bool start;

        public FormLiveControl()
        {
            AddEvent();

            AddClipboardFormatListener(Handle);

            Closed += FormLiveControl_Closed;
            InitializeComponent();

            Load += FormLiveControl_Load;
            ButtonsInit();
            _policy = new MousePolicy(this);
        }

        private LiveControlManager LiveControlManager => NovaClient.Instance.LiveControlManager;
        private FileTransferSeverManager FileTransferSeverManager => NovaClient.Instance.FileTransferSeverManager;
        public FileTransferCilentManager FileTransferCilentManager => NovaClient.Instance.FileTransferCilentManager;

        public Rectangle CoustomBounds
        {
            get => gdiScreen1.Bounds;
            set
            {
                var widthChange = value.Width - gdiScreen1.Width;
                Width += widthChange;
                Height += value.Height - gdiScreen1.Height;
                Top = value.Top;
                Left = value.Left;
            }
        }
        const int WM_SYSCOMMAND = 0x112;
        const int SC_CLOSE = 0xF060;
        const int SC_MINIMIZE = 0xF020;
        const int SC_MAXIMIZE = 0xF030;
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_SYSCOMMAND)
            {
                if (m.WParam.ToInt32() == SC_MINIMIZE) //是否点击最小化
                {
                    LiveControlManager.Minimize();
                    return;
                }
                if (m.WParam.ToInt32() == SC_MAXIMIZE) //是否点击最大化
                {
                    LiveControlManager.Maximinze();
                    return;
                    //.....................
                }
                if (m.WParam.ToInt32() == SC_CLOSE) //是否点击关闭
                {
                    //.....................}

                }
            }
            base.WndProc(ref m);
        }

        protected override void DefWndProc(ref Message m)
        {
            if (m.Msg == WM_CLIPBOARDUPDATE)
            {
                if (Clipboard.ContainsFileDropList())
                    FileTransferSeverManager.NotifyNewFileCanCopy(Clipboard.GetFileDropList());
                else if (Clipboard.ContainsText())
                    FileTransferSeverManager.NotifyNewTextCanCopy(Clipboard.GetText());
            }
            else
            {
                base.DefWndProc(ref m);
            }
        }

        private void AddEvent()
        {
            LiveControlManager.OnScreenshotReceived += LiveControlManager_OnScreenshotReceived;
            LiveControlManager.OnRectChangeReceived += LiveControlManager_OnRectChangeReceived;
            LiveControlManager.OnPickedStatusChanged += LiveControlManager_OnPickedStatusChanged;
            LiveControlManager.OnCursorMessageEvent += LiveControlManager_OnCursorMessageEvent;
            LiveControlManager.Network.OnDisconnected += Network_OnDisconnected;
            LiveControlManager.OnServerShutdown += LiveControlManager_OnServerShutdown;
            LiveControlManager.OnFormListReceived += LiveControlManager_OnFormListReceived;
            LiveControlManager.OnReceivedRefuseControlMessage += LiveControlManager_OnReceivedRefuseControlMessage;
            LiveControlManager.OnReceivedRequestReCoverControlMessge +=
                LiveControlManager_OnReceivedRequestReCoverControlMessge;
            LiveControlManager.OnReceivedGetControl += LiveControlManager_OnReceivedGetControl;
            LiveControlManager.OnReceivedLoseControl += LiveControlManager_OnReceivedLoseControl;
            LiveControlManager.OnSeverStatusReceived += LiveControlManager_OnSeverStatusReceived;
            LiveControlManager.OnResponseScreenRectReceived += LiveControlManager_OnResponseScreenRectReceived;
            LiveControlManager.OnReceivedRefuseControlApplyMessage +=
                LiveControlManager_OnReceivedRefuseControlApplyMessage;
            LiveControlManager.OnReceivedFormClosed += LiveControlManager_OnReceivedFormClosed;
            LiveControlManager.OnReceivedFormMinisized += LiveControlManager_OnReceivedFormMinisized;
        }

        private void DeleteEvent()
        {
            LiveControlManager.OnScreenshotReceived -= LiveControlManager_OnScreenshotReceived;
            LiveControlManager.OnRectChangeReceived -= LiveControlManager_OnRectChangeReceived;
            LiveControlManager.OnPickedStatusChanged -= LiveControlManager_OnPickedStatusChanged;
            LiveControlManager.OnCursorMessageEvent -= LiveControlManager_OnCursorMessageEvent;
            LiveControlManager.Network.OnDisconnected -= Network_OnDisconnected;
            LiveControlManager.OnServerShutdown -= LiveControlManager_OnServerShutdown;
            LiveControlManager.OnFormListReceived -= LiveControlManager_OnFormListReceived;
            LiveControlManager.OnReceivedRefuseControlMessage -= LiveControlManager_OnReceivedRefuseControlMessage;
            LiveControlManager.OnReceivedRequestReCoverControlMessge -=
                LiveControlManager_OnReceivedRequestReCoverControlMessge;
            LiveControlManager.OnReceivedGetControl -= LiveControlManager_OnReceivedGetControl;
            LiveControlManager.OnReceivedLoseControl -= LiveControlManager_OnReceivedLoseControl;
            LiveControlManager.OnSeverStatusReceived -= LiveControlManager_OnSeverStatusReceived;
            LiveControlManager.OnResponseScreenRectReceived -= LiveControlManager_OnResponseScreenRectReceived;
            LiveControlManager.OnReceivedRefuseControlApplyMessage -=
                LiveControlManager_OnReceivedRefuseControlApplyMessage;
            LiveControlManager.OnReceivedFormClosed -= LiveControlManager_OnReceivedFormClosed;
            LiveControlManager.OnReceivedFormMinisized -= LiveControlManager_OnReceivedFormMinisized;
        }

        private void LiveControlManager_OnReceivedFormMinisized(object sender, S2CTellClientFormMinisizedMessage e)
        {
            var ret = MessageBox.Show(this, $@"目标窗口被最小化，是否恢复窗口。", @"是否恢复窗口", MessageBoxButtons.YesNo);
            switch (ret)
            {
                case DialogResult.Yes:
                    LiveControlManager.ReshowShareForm(e.FormItemModel);
                    break;
                case DialogResult.No:
                default:
                    CloseType = CloseType.ShareFormMiniSize;
                    Close();
                    break;
            }
        }

        private void LiveControlManager_OnReceivedFormClosed(object sender, S2CTellClientFormClosedMessage e)
        {
            CloseType = CloseType.ShareFormClose;
            Close();
        }

        private void LiveControlManager_OnReceivedLoseControl(object sender,
            S2CTellClientYourControlBeRecoveredMessage e)
        {
            MessageBox.Show(this, $@"你已失去控制权，如需重新获取，请双击远程桌面，并按提示操作。", @"控制请求", MessageBoxButtons.OK);
        }

        private void LiveControlManager_OnReceivedGetControl(object sender, S2CTellClientYouGetTheControl e)
        {
            MessageBox.Show(this, $@"你现在拥有控制权，可直接对远程桌面进行操作。", @"获得控制权", MessageBoxButtons.OK);
        }

        private void LiveControlManager_OnReceivedRequestReCoverControlMessge(object sender,
            S2CRequestRecoverControlMessage e)
        {
            if (e.FlagForce)
                return;
            var tMessageBox = new ControlApplyMessageBox();
            var ret = tMessageBox.ShowDialog(this);
            var tMessage = new C2SResponseControlResultMessge();
            switch (ret)
            {
                case DialogResult.Yes:
                    tMessage.Result = ResultType.Allow;
                    break;
                case DialogResult.No:
                default:
                    tMessage.Result = ResultType.Refuse;
                    tMessage.RefuseReason = tMessageBox.Reason;
                    break;
            }

            LiveControlManager.SendResultMessage(tMessage);
            tMessageBox.Dispose();
        }


        private void LiveControlManager_OnReceivedRefuseControlApplyMessage(object sender,
            S2CRefuseControlApplyMessage e)
        {
            // 申请控制被拒绝了
            MessageBox.Show(this, $@"你的控制申请已被目标机器拒绝，拒绝理由：{e.RefuseReason}", @"控制请求被拒绝", MessageBoxButtons.OK);
        }

        private void LiveControlManager_OnReceivedRefuseControlMessage(object sender, S2CRefuseControlMessage e)
        {
            switch (e.MouseType)
            {
                case MouseType.MouseDown:
                    toolTip1.Show(@"当前未获取控制权限，双击可申请控制权限！", this);
                    break;
                case MouseType.DoubleClick:
                    var ret = MessageBox.Show(this, @"目标机器被其它机器控制，是否需要强行取得控制权：
    '是'强行取得控制;
    '否'申请控制权;
    '取消'取消操作。", @"是否强行取得控制权", MessageBoxButtons.YesNoCancel);

                    switch (ret)
                    {
                        case DialogResult.Cancel:
                            break;
                        case DialogResult.Yes:
                            LiveControlManager.RequsetControlByForce();
                            break;
                        case DialogResult.No:
                            LiveControlManager.RequestControlByApply();
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void FormLiveControl_Load(object sender, EventArgs e)
        {
            LiveControlManager.RequstFormList();
        }

        private void ButtonsInit()
        {
            _btnArry[0] = btn_1;
            _btnArry[1] = btn_2;
            _btnArry[2] = btn_3;
            _btnArry[3] = btn_4;

            btn_1.Hide();
            btn_2.Hide();
            btn_3.Hide();
            btn_4.Hide();

            btn_1.Click += FormButton_Click;
            btn_2.Click += FormButton_Click;
            btn_3.Click += FormButton_Click;
            btn_4.Click += FormButton_Click;
        }

        private void FormButton_Click(object sender, EventArgs e)
        {
            var btn = sender as Button;
            if (btn?.Tag == null) return;

            var model = (FormItemModel) btn.Tag;
            LiveControlManager.ConnectToShareForm(model);
            this.Text = model.ExeFormName;
            if (!_flagEventInit)
                InitEvent();
            HideButtons();
        }

        private void LiveControlManager_OnFormListReceived(object sender, S2CResponseShareFormListMessage e)
        {
            if (start)
                return;
            var models = e.FormItemModels;
            for (var i = 0; i < models.Length; i++)
            {
                var formItemModel = models[i];
                if (formItemModel.Ptr == IntPtr.Zero)
                {
                    _btnArry[i].Enabled = false;
                    _btnArry[i].Show();
                    _btnArry[i].Text = @"窗口未开启";
                    _btnArry[i].BackColor = Color.Gray;
                    continue;
                }

                if (formItemModel.FlagOccupied)
                    _btnArry[i].Text = $@"窗口:{formItemModel.ExeFormName}[已连接]";
                else
                    _btnArry[i].Text = $@"窗口:{formItemModel.ExeFormName}";
                _btnArry[i].Tag = formItemModel;
                _btnArry[i].Enabled = true;
                _btnArry[i].BackColor = Color.Red;
                _btnArry[i].Show();
            }
        }

        private void LiveControlManager_OnServerShutdown(object sender, KnowladgeServerMachineShutDown e)
        {
            CloseType = CloseType.ServerClose;
            Close();
        }

        private void Network_OnDisconnected(object sender, DisconnectedEventArgs e)
        {
            CloseType = CloseType.Disconnect;
            Close();
        }

        private void LiveControlManager_OnResponseScreenRectReceived(object sender, ResponseScreenRectMessage e)
        {
            gdiScreen1.SetRect(e.Bounds);
            _policy.ScreenRectangle = e.Bounds;
        }

        private void LiveControlManager_OnCursorMessageEvent(object sender, CursorMessage e)
        {
            if (!menuItemSelForm.FlagActive)
                Cursor = e.Cursor;
        }

        //private Cursor _tempCursor = null;
        private void FormLiveControl_Deactivate(object sender, EventArgs e)
        {
            ReleaseSca();
        }

        private void GdiScreen1_MouseEnter(object sender, EventArgs e)
        {
            //Cursor = _tempCursor ?? DefaultCursor;
        }


        private void ReleaseSca()
        {
            LiveControlManager.NotifyKeyUp(new KeyEventArgs(Keys.ShiftKey));
            LiveControlManager.NotifyKeyUp(new KeyEventArgs(Keys.ControlKey));
            LiveControlManager.NotifyKeyUp(new KeyEventArgs(Keys.Menu));
        }


        private void LiveControlManager_OnSeverStatusReceived(object sender, ServerStatusMessage e)
        {
            menuList1.ListCount = e.ScreenCount;
        }

        /// <summary>
        ///     获取到共享窗口大小的变更
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LiveControlManager_OnRectChangeReceived(object sender, RectChangeMessageEventArgs e)
        {
            if (!LiveControlManager.FlagShowFullScreen)
            {
                gdiScreen1.OnlyShowProc(GetRegion(e.Rect), ScreenStatus.OnlyShowProcAndFit);
                //todo 设置鼠标警察
                _policy.FlagOnlyShowForm = true;
                _policy.SetRealFormRect(e.Rect.Rect.ToRectangle());
            }
        }

        private Rectangle GetRegion(IdRect rect)
        {
            var tRect = new Rectangle(rect.Rect.Left, rect.Rect.Top, rect.Rect.Right - rect.Rect.Left,
                rect.Rect.Bottom - rect.Rect.Top);
            return tRect;
        }

        private void FormLiveControl_Closed(object sender, EventArgs e)
        {
            LiveControlManager.ClearConnection();
            LiveControlManager.Provider.StopTick();
            RemoveClipboardFormatListener(Handle);
            DeleteEvent();
        }

        private void LiveControlManager_OnScreenshotReceived(object sender, ScreenshotMessageEventArgs e)
        {
            var screenshot = e.Screenshot;
            try
            {
                using (var stream = new MemoryStream(screenshot.Image))
                {
                    var image = Image.FromStream(stream);
                    gdiScreen1.Draw(image, screenshot.Region, _showRegionOutlines);
                }
                //if (e.Screenshot.FrameIndex != currentFramenum)
                //{
                gdiScreen1.Invalidate();
                currentFramenum = e.Screenshot.FrameIndex;
                //}
            }
            catch (Exception)
            {
            }

            LiveControlManager.NeedNewScreenshot();

            Trace.WriteLine(
                $"Processed Image #{e.Screenshot.Number}, Size: {GetKbFromBytes(e.Screenshot.Image.Length)} KB");
        }

        private static float GetKbFromBytes(long bytes)
        {
            return bytes / (float) 1024;
        }

        private static float GetTotalScreenshotsKb(List<Screenshot> screenshots)
        {
            var total = 0f;
            screenshots.ForEach(x => { total += GetKbFromBytes(x.Image.Length); });
            return total;
        }


        private void HideButtons()
        {
            ButtonRequestScreenshot.Hide();
            btn_1.Hide();
            btn_2.Hide();
            btn_3.Hide();
            btn_4.Hide();
            start = true;
        }

        private void ButtonRequestScreenshot_Click(object sender, EventArgs e)
        {
            HideButtons();
            flowLayoutPanel1.Visible = true;
            LiveControlManager.ResetScreenIndex();
            LiveControlManager.RequestScreenshot();
            LiveControlManager.Provider.StartTick();
            if (!_flagEventInit)
                InitEvent();
        }

        private void InitEvent()
        {
            gdiScreen1.KeyDown += gdiScreen1_KeyDown;
            gdiScreen1.KeyPress += gdiScreen1_KeyPress;
            gdiScreen1.KeyUp += gdiScreen1_KeyUp;
            gdiScreen1.MouseDoubleClick += gdiScreen1_MouseDoubleClick;
            gdiScreen1.MouseDown += gdiScreen1_MouseDown;
            gdiScreen1.MouseMove += gdiScreen1_MouseMove;
            gdiScreen1.MouseUp += gdiScreen1_MouseUp;
            gdiScreen1.MouseWheel += gdiScreen1_MouseWheel;
            gdiScreen1.ImeMode = ImeMode.Off;
            gdiScreen1.MouseEnter += GdiScreen1_MouseEnter;
            menuList1.OnCurrentIndexChanged += MenuList1_OnCurrentIndexChanged;
            Deactivate += FormLiveControl_Deactivate;

            _flagEventInit = true;
        }


        private void MenuList1_OnCurrentIndexChanged(object sender, IndexChangedEventArgs e)
        {
            LiveControlManager.SetCurrentScreenIndex(e.SelIndex);
            gdiScreen1.Focus();
        }

        /// <summary>
        ///     当状态发生变化是，更改前端状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LiveControlManager_OnPickedStatusChanged(object sender, WindowPickedStatus e)
        {
            Cursor = Cursors.Default;
            if (e.FlagFullScreen)
            {
                //btnGetWindow.Text = "获取窗口";
                menuItemSelForm.FlagActive = false;
                gdiScreen1.ShowFullScreen();

                //todo 还原鼠标警察
                _policy.FlagOnlyShowForm = false;
                _policy.ResetBound();
            }
        }

        private void menuItemSelForm_OnChecked(object sender, EventArgs e)
        {
            _flagGetWindow = true;
            Cursor = Cursors.Cross;
            menuItemSelForm.ToolTipText = "当前状态为单窗显示状态，点击该按钮可切换为全屏显示。";
        }

        private void menuItemSelForm_OnUnchecked(object sender, EventArgs e)
        {
            _flagGetWindow = false;
            LiveControlManager.SetCurrentProcId(-1);
            Cursor = Cursors.Default;
            menuItemSelForm.ToolTipText = "当前状态为全屏状态，点击该按钮可选取单窗进行显示。";
        }

        #region 鼠标键盘事件

        private void gdiScreen1_MouseWheel(object sender, MouseEventArgs e)
        {
            LiveControlManager.NotifyMouseWheel(_policy.MouseArgsRevise(e));
            Trace.WriteLine("鼠标MouseWheel");
        }


        private void gdiScreen1_MouseMove(object sender, MouseEventArgs e)
        {
            gdiScreen1.ReAlignPos(e);
            LiveControlManager.NotifyMouseMove(_policy.MouseArgsRevise(e));
            Trace.WriteLine("鼠标MouseMove");
        }

        private void gdiScreen1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            LiveControlManager.NotifyMouseDoubleClick(_policy.MouseArgsRevise(e));
            Trace.WriteLine("鼠标gdiScreen1_MouseDoubleClick");
        }

        private void gdiScreen1_MouseDown(object sender, MouseEventArgs e)
        {
            LiveControlManager.NotifyMouseDown(_policy.MouseArgsRevise(e));
            gdiScreen1.Focus();
            toolTip1.Show(@"", gdiScreen1);
            Trace.WriteLine("鼠标gdiScreen1_MouseDown");
        }

        private void gdiScreen1_MouseUp(object sender, MouseEventArgs e)
        {
            if (_flagGetWindow)
            {
                LiveControlManager.NotifyGetWindow(_policy.MouseArgsRevise(e));
                _flagGetWindow = false;
            }
            LiveControlManager.NotifyMouseUp(_policy.MouseArgsRevise(e));
            Trace.WriteLine("鼠标gdiScreen1_MouseUp");
        }

        private void gdiScreen1_KeyDown(object sender, KeyEventArgs e)
        {
            LiveControlManager.NotifyKeyDown(e);
            Trace.WriteLine("gdiScreen1_KeyDown");
        }

        private void gdiScreen1_KeyPress(object sender, KeyPressEventArgs e)
        {
            LiveControlManager.NotifyKeyPress(e);
            Trace.WriteLine("gdiScreen1_KeyPress");
        }

        private void gdiScreen1_KeyUp(object sender, KeyEventArgs e)
        {
            LiveControlManager.NotifyKeyUp(e);
            Trace.WriteLine("gdiScreen1_KeyUp");
        }

        #endregion
    }

    public enum CloseType
    {
        ServerClose,
        Disconnect,
        UserClose,
        ShareFormClose,
        ShareFormMiniSize
    }
}